package ch.schlegl.fastsvn;

import ch.schlegl.fastsvn.json.Revision;
import ch.schlegl.fastsvn.json.RevisionsResult;
import org.tmatesoft.svn.core.*;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.lang.Math.max;
import static java.lang.Math.min;

@Path("revisions")
public class SvnRevisionsResource extends SvnClient {
    private static final Long MAX_NUM_REVISIONS = 1000L;

    public SvnRevisionsResource() throws IOException {
        super();
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public synchronized RevisionsResult getRevisions(@QueryParam("id") String id,
                                        @QueryParam("node") String node,
                                        @QueryParam("offset") Long offset,
                                        @QueryParam("count") Long count,
                                        @QueryParam("page") Long page) {
        List<Revision> revisions = new ArrayList<Revision>();
        System.out.println(id + " -> " + node);
        Long latestRevision = 0L;
        if("root".equals(id) || "".equals(id) || id == null) {
            revisions.add(new Revision(0L, " - no revisions - ", "", true, ""));
            return new RevisionsResult(revisions, 1L);
        }

        try {
            ensureConnected(id);

            String path = "";

            String rootUrl = findRootUrl(id);
            int idx = node.indexOf(rootUrl);
            if (idx >= 0) {
                path = node.substring(idx + rootUrl.length());
            }
            System.out.println("path: " + path);

            latestRevision = repository.getLatestRevision();
            Long toRevision = min(latestRevision - offset, latestRevision);
            Long fromRevision = max(0, latestRevision - offset - count);
            if (toRevision - fromRevision > MAX_NUM_REVISIONS) {
                fromRevision = toRevision - MAX_NUM_REVISIONS;
            }
            System.out.println("Rev. " + fromRevision + " to " + toRevision);

            //Collection entries = new ArrayList<String>();
            Collection<SVNLogEntry> logs = repository.log(new String[]{""}, null, fromRevision, toRevision, true, true);
            for (SVNLogEntry logEntry : logs) {
                revisions.add(new Revision(logEntry.getRevision(), linkIds(logEntry, rootUrl), logEntry.getAuthor(), matches(logEntry, path), logEntry.getDate().toString()));
            }
            Collections.reverse(revisions);
        } catch (SVNException e) {
            e.printStackTrace();
        }
        return new RevisionsResult(revisions, latestRevision);
    }

    private String linkIds(SVNLogEntry logEntry, String url) {
        String message = logEntry.getMessage();
        if (message != null && !message.isEmpty()) {
            for (Map.Entry<String, String> entry : svnConfigsByUrl.get(url).getMessagePatterns().entrySet()) {
                String prefixPattern = entry.getKey();
                String linkPattern = entry.getValue();
                message = parseMessageIds(message, prefixPattern, linkPattern);
            }
        }
        return message;
    }

    String parseMessageIds(String message, String prefixPattern, String linkPattern) {
        String parsedMessage = message;
        String plainPrefix = prefixPattern.replaceAll("\\[", "").replaceAll("]", "");
        Pattern intsOnly = Pattern.compile(prefixPattern + "\\d+");
        Matcher makeMatch = intsOnly.matcher(message);
        while (!makeMatch.hitEnd() && makeMatch.find()) {
            String inputInt = makeMatch.group();
            try {
                String intString = inputInt.substring(plainPrefix.length());
                if (intString.length() < String.valueOf(Long.MAX_VALUE)
                        .length() - 1) {
                    Long id = Long.valueOf(intString);
                    String storyLink = String.format("<a href=\"" + linkPattern + "\" target=\"_blank\">%2$s%1$s</a>", id, plainPrefix);
                    parsedMessage = parsedMessage.replaceAll(inputInt, storyLink);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return parsedMessage;
    }

    private boolean matches(SVNLogEntry logEntry, String path) {
        //System.out.println("checking " + path);
        for (String changedPath : logEntry.getChangedPaths().keySet()) {
            //System.out.println(changedPath + " vs. " + path);
            if (changedPath.startsWith(path)) {
                return true;
            }
        }
        return false;
    }
}